#include <allegro.h>
#include "hero.hpp"
#include "main.hpp"
#include <vector>
#include <cmath>
#include "eq.hpp"
#include "parser.hpp"

#include <iostream>

cHero::cHero(int x, int y, cItemResources * resPointer)
{
    pos = new sPoint(x, y);
    speed = 4;
    frame = 0;

    curTask = NULL;

    img = load_bmp("GFX\\hero\\hero.bmp", default_palette);
    tmp = create_bitmap(TILE_SIZE, TILE_SIZE);
    clear_to_color(tmp, 0xFF00FF);

    eq = new cEQ(0, 0, 5, 12, resPointer);
    stuff = new cStuff(130, 0, resPointer);
    fWay = new cFindWay;

    questContainer = new cQuestContainer;

    range = {{0, 0, 0, 1, 1, 1, 0, 0, 0},
             {0, 0, 1, 1, 1, 1, 1, 0, 0},
             {0, 1, 1, 1, 1, 1, 1, 1, 0},
             {1, 1, 1, 1, 1, 1, 1, 1, 1},
             {1, 1, 1, 1, 1, 1, 1, 1, 1},
             {1, 1, 1, 1, 1, 1, 1, 1, 1},
             {0, 1, 1, 1, 1, 1, 1, 1, 0},
             {0, 0, 1, 1, 1, 1, 1, 0, 0},
             {0, 0, 0, 1, 1, 1, 0, 0, 0}};
}

cHero::~cHero()
{
    destroy_bitmap(img);
    destroy_bitmap(tmp);
    delete pos;
    delete curTask;

    for (int i = 0; i < task.size(); i++)
        delete task[i];
}

void cHero::setWay(std::vector <sPoint> way)
{
    if (way.size() == 0) return;
    clearTasks();
    delete curTask;
    curTask = NULL;

    for (int i = way.size() - 1; i >= 0; i--)
    {
        cTask * t = new cTask(way[i].x, way[i].y, TASK_MOVE);
        task.push_back(t);
    }
}

void cHero::update(sPoint * show, cMap * map, BITMAP * bufor, bool &MOUSE_FREE)
{
    //wykonanie obecnych zadan
    if (task.size() > 0 || curTask != NULL)
    {
        //jesli obecnie rozpatrywany wskaznik nie ma wartosci
        if (curTask == NULL)
        {
            //to dajemy do neigo pierwsze zadanie
            curTask = task[0];
            task.erase(task.begin());

            frame = 0;//ustawiamy index klatki na 0
            angle = -countAngle(curTask->x - pos->x, curTask->y - pos->y);
        }

        switch (curTask->type)
        {
            case TASK_MOVE:
            {
                if (map->fogOn) actualiseFog(map);

                if (move(curTask->x, curTask->y))
                {
                    delTask_getNext();
                }
                break;
            }

            case TASK_ATTACK: break;

            case TASK_TALK:
            {
                talkWithNPC(bufor, (cNPC*)(int)curTask->x, MOUSE_FREE);

                delTask_getNext();
            }
            break;

            case TASK_PICKUP_ITEM://PODNOSZENIE ITEMA Z ZIEMII
            {
                eq->itemAdd(map->items[curTask->x].id, map->items[curTask->x].count);
                map->items.erase(map->items.begin() + curTask->x);

                delTask_getNext();
            }
            break;

            case TASK_TRANSITION: //PRZEJSCIE NA KOLEJNA MAPE
            {
                map->delete_current_map();
                map->load(map_to_load.c_str());
                std::cout<< "Przechodzę na mapę: " << map_to_load.c_str();
                delTask_getNext();
            }
            break;


        }
    }

    frame += 0.3;
    if (frame > MAX_FRAME) frame = 0;
}

void cHero::actualiseFog(cMap * map)
{
    for (int i = 0; i < 9; i++)
        for (int j = 0; j < 9; j++)
            if (range[i][j])
                if (map->isOnMap(sPoint((int)(i+pos->x/TILE_SIZE-4), (int)(j+pos->y/TILE_SIZE-4)) * TILE_SIZE))
                    map->fogOfWar[(int)(i-4+pos->x/TILE_SIZE)][(int)(j-4+pos->y/TILE_SIZE)] = true;
}

void cHero::clearTasks()
{
    for (int i = 0; i < task.size(); i++)
        delete task[i];

    task.clear();
}

void cHero::delTask_getNext()
{
    delete curTask;
    curTask = NULL;

    if (task.size() > 0)
    {
        curTask = task[0];
        task.erase(task.begin());
        angle = -countAngle(curTask->x - pos->x, curTask->y - pos->y);
    }
}

bool cHero::move(float x, float y)
{
    //jesli jestesmy w tym punkcie to koniec funkcji
    if (pos->x == x && pos->y == y) return true;

    //jesli roznica pomiedzy punktem a celem < jego krok to przesuwamy ją na ten punkt
    if (abs(disBetweenPoint(pos->x, pos->y, x, y)) < speed)
    {
        pos->x = x;
        pos->y = y;
        return true;
    }

    //przesuwamy postac
    pos->x += cos(angle) * speed;//liczymy przesuniecie X
    pos->y += sin(angle) * speed;//liczymy przesuniecie Y

    //jesli jestesmy w prawidlowym punkcie to zwracamy prawde (kolejny task)
    if (pos->x == x && pos->y == y) return true;

    return false;
}

void cHero::draw(BITMAP * bufor, sPoint * show, sPoint * shift)
{
    int action = 0;

    if (curTask == NULL) action = TASK_NOTHING;
    else action = curTask->type;

    blit(img, tmp, action * TILE_SIZE, (int)(frame) * TILE_SIZE, 0, 0, TILE_SIZE, TILE_SIZE);

    int X = pos->x - show->x;
    int Y = pos->y - show->y;


    if (action == TASK_NOTHING) angle = -countAngle(mouse_x - shift->x - X, mouse_y - shift->y - Y);

    rotate_sprite(bufor, tmp, X - TILE_SIZE/2 + shift->x, Y - TILE_SIZE/2 + shift->y, ftofix(rtoa(angle) * 255 / 360));

    if (eq->isDrawing)
    {
        stuff->draw(bufor);
        eq->draw(bufor, -1, -1);
    }

    //takie tam szybkie rysowanie sciezki bohatera
    if (false && action != TASK_NOTHING)
    {
        rectfill(bufor, curTask->x - 3 - show->x, curTask->y - 3 - show->y, curTask->x + 3 - show->x, curTask->y + 3 - show->y, 0xFF0000);
        line(bufor, pos->x - show->x, pos->y - show->y, curTask->x - show->x, curTask->y - show->y, 0xFF0000);

        if (task.size() > 0)
        {
            line(bufor, curTask->x - show->x, curTask->y - show->y, task[0]->x - show->x, task[0]->y - show->y, 0xFF0000);

            for (int i = 0; i < task.size()-1; i++)
            {
                rectfill(bufor, task[i]->x - 3 - show->x, task[i]->y - 3 - show->y, task[i]->x + 3 - show->x, task[i]->y + 3 - show->y, 0xFF0000);
                line(bufor, task[i]->x - show->x, task[i]->y - show->y, task[i+1]->x - show->x, task[i+1]->y - show->y, 0xFF0000);
            }

            rectfill(bufor, task[task.size()-1]->x - 3 - show->x, task[task.size()-1]->y - 3 - show->y, task[task.size()-1]->x + 3 - show->x, task[task.size()-1]->y + 3 - show->y, 0xFF0000);
        }
    }
}

void cHero::checkMouse(int mouse_x, int mouse_y, bool &MOUSE_FREE, cMap * map, sPoint * show, std::vector <cNPC*> * NPClist)
{
    //jesli nacisniemy LPM
    if (mouse_b == 1)
    {
        if (MOUSE_FREE)//jesli myszka jest wolna
        {
            //sprawdzamy czy nie nacisnelismy na kafel ktory jest kolizją
            if (!map->tile[map->map[(int)(mouse_x+show->x - map->shift->x)/TILE_SIZE][(int)(mouse_y+show->y - map->shift->y)/TILE_SIZE]].wall)
            {
                if (map->map_green[(int)(mouse_x+show->x - map->shift->x)/TILE_SIZE][(int)(mouse_y+show->y - map->shift->y)/TILE_SIZE] == -1)
                {
                     if (!map->fogOn || map->fogOfWar[(int)(mouse_x+show->x - map->shift->x)/TILE_SIZE][(int)(mouse_y+show->y - map->shift->y)/TILE_SIZE])
                    {
                        bool isNPC = false, isItem = false, isMoob = false;
                        int NPCID, itemID;

                        //sprawdzamy czy jest tam jakis item
                        for (int i = 0; i < map->items.size(); i++)
                        {
                            if (mouse_x - map->shift->x >= map->items[i].x - ITEM_SIZE/2 && mouse_x - map->shift->x < map->items[i].x + ITEM_SIZE/2)
                            {
                                if (mouse_y - map->shift->y >= map->items[i].y - ITEM_SIZE/2 && mouse_y - map->shift->y < map->items[i].y + ITEM_SIZE/2)
                                {
                                    isItem = true;
                                    itemID = i;
                                    break;
                                }
                            }
                        }

                        //sprawdzamy czy jest tam jakis NPC
                        for (int i = 0; i < NPClist[0].size(); i++)
                        {
                            if (mouse_x - map->shift->x >= NPClist[0][i]->pos->x - ITEM_SIZE/2 && mouse_x - map->shift->x < NPClist[0][i]->pos->x + ITEM_SIZE/2)
                            {
                                if (mouse_y - map->shift->y >= NPClist[0][i]->pos->y - ITEM_SIZE/2 && mouse_y - map->shift->y < NPClist[0][i]->pos->y + ITEM_SIZE/2)
                                {
                                    isNPC = true;
                                    NPCID = i;
                                    break;
                                }
                            }
                        }

                        //sprawdzamy czy jest tam przejscie TP

                        for (int i = 0 ; i < map->countoftransitions * map->countoftransitions ; i+=3)
                        {
                            if (int(mouse_x /  TILE_SIZE) == atoi(map->transition[i].c_str()) && int(mouse_y /  TILE_SIZE) == atoi(map->transition[i+1].c_str()))
                            {
                            map_to_load =  "Data\\Maps\\" + map->transition[i+2] + ".map";
                            cTask * t = new cTask(itemID, 0, TASK_TRANSITION);
                            std::cout <<"TU STOI PRZEJSCIE " << map_to_load.c_str();
                            addTask(t);
                            }
                        }


                        if (isItem)
                        {
                            std::vector <sPoint> way = fWay->findWay(map, sPoint(pos->x, pos->y), sPoint(mouse_x + show->x - map->shift->x, mouse_y + show->y - map->shift->y));
                            setWay(way);
                            cTask * t = new cTask(itemID, 0, TASK_PICKUP_ITEM);
                            addTask(t);
                        }
                        else if (isNPC)
                        {
                            float angle = countAngle(pos->x - (mouse_x + show->x - map->shift->x), pos->y - (mouse_y + show->y - map->shift->y));
                            std::vector <sPoint> way = fWay->findWay(map, sPoint(pos->x, pos->y), sPoint(mouse_x + show->x - map->shift->x + cos(angle) * TILE_SIZE, mouse_y + show->y - map->shift->y - sin(angle) * TILE_SIZE));
                            setWay(way);
                            cTask * t = new cTask((int)NPClist[0][NPCID], 0, TASK_TALK);
                            addTask(t);
                        }else
                        {
                            //wyszukujemy droge
                            std::vector <sPoint> way = fWay->findWay(map, sPoint(pos->x, pos->y), sPoint(mouse_x + show->x - map->shift->x, mouse_y + show->y - map->shift->y));
                            setWay(way);
                        }
                    }
                }
            }
        }

        MOUSE_FREE = false;//myszka jest wcisnieta, bo mouse_b == 1

    }else MOUSE_FREE = true;//jesli mouse_b != 1 (moga byc inne wcisniete, ale to szczegół) to myszka jest wolna
}

void cHero::addTask(cTask * t)
{
    task.push_back(t);
}

void cHero::talkWithNPC(BITMAP * bufor, cNPC * npc, bool &MOUSE_FREE, cDialog * d)
{
    int h;
    cDialog * dial;

    if (d == NULL)
        dial = npc->dialog;//obecnie rozpatrywany i rysowany dialog
    else
        dial = d;

    h = 1 + dial->subDialog.size() * 12;
    int y;

    //std::cout << "DIALOG #\n";
    //std::cout << "Powiedziales: " << dial->heroDial << "\n";
    //std::cout << "Odpowiedz NPC: " << dial->npcDial << "\n\n";

    while (!key[KEY_ESC])
    {
        if (mouse_b == 1)
        {
            if (MOUSE_FREE)
            {
                MOUSE_FREE = false;
                if (mouse_y < bufor->h - h - 14) break;

                if (mouse_y > bufor->h - h - 1 && mouse_y < bufor->h - 2)
                {
                    int id = ((mouse_y - (bufor->h - h + 1)) / 12);
                    drawGame();

                    if (dial->subDialog[id]->heroDial == "Handel")
                    {
                        tradeWithNPC(bufor, npc, MOUSE_FREE);
                        drawGame();
                    }
                    else
                    {
                        if (dial->subDialog[id]->heroDial == "Wroc") {return;}
                        if (dial->subDialog[id]->heroDial == "Koniec") {return;}
                        talkWithNPC(bufor, npc, MOUSE_FREE, dial->subDialog[id]);
                        drawGame();
                    }
                }
            }
        } else MOUSE_FREE = true;

        drawGame();
        //okno odpowiedzi
        rectfill(bufor, 1, bufor->h - h - 13, bufor->w-2, bufor->h - h - 2, 0xa07f58);//pole na text
        rect(bufor, 0, bufor->h - h - 14, bufor->w-1, bufor->h - h - 1, 0x5c4c3a);//obranowka

        //okno z wypowiedziami
        rectfill(bufor, 1, bufor->h - h, bufor->w-2, bufor->h-2, 0xa07f58);//pole na text
        rect(bufor, 0, bufor->h - h - 1, bufor->w-1, bufor->h-1, 0x5c4c3a);//obramowka

        //wypisanie kwestii NPC
        textprintf_ex(bufor, font, 10, bufor->h - h - 11, 0x5c4c3a, -1, "« %s", dial->npcDial.c_str());

        if (mouse_y > bufor->h - h - 1 && mouse_y < bufor->h - 2)
        {
            rectfill(bufor, 5,
                     ((mouse_y - (bufor->h - h + 1)) / 12) * 12 + bufor->h - h,
                     bufor->w - 6,
                     ((mouse_y - (bufor->h - h + 1)) / 12) * 12 + bufor->h - h + 11,
                     0x91714c);
        }

        //wypisanie mozliwych odpowiedz gracza
        for (int i = 0; i < dial->subDialog.size(); i++)
            textprintf_ex(bufor, font, 10, bufor->h - h + i * 10 + (i+1) * 2, 0x5c4c3a, -1, "» %s", dial->subDialog[i]->heroDial.c_str());

        //przerzucenie calosci na ekran
        blit(bufor, screen, 0, 0, 0, 0, bufor->w, bufor->h);//bufor->h - h - 14, 0, bufor->h - h - 14, bufor->w, h + 14);

        rest(20);
    }

    //std::cout << "Wychodzisz z dialogu (" << dial->npcDial << ")\n";
}

void cHero::tradeWithNPC(BITMAP * bufor, cNPC * npc, bool &MOUSE_FREE)
{
    sPoint npcEQ(bufor->w - npc->eq->size->x * ITEM_SIZE - 1, 0);
    sPoint heroEQ(0, 0);
    npc->eq->isDrawing = false;
    eq->isDrawing = false;

    sPoint selItem(-1, -1);
    bool fromHero = false;

    while (!key[KEY_ESC])
    {
        if (key[KEY_SPACE]) break;
        ///PORUSZANIE SIE PO HANDLU
        // przenoszenie itemka z eq shopa do eq bohatera i na odwrot
        checkDragItem(eq, npc->eq, MOUSE_FREE, selItem, fromHero);

        //myszka nad itemkiem (wyswietlanie jego informacji)
        checkItemInfo(npc->eq);
        checkItemInfo(eq);

        ///RYSOWANIE EQ I ITEMA PRZENOSZONEGO
        drawGame();//rysuje gre
        eq->draw(bufor, heroEQ.x, heroEQ.y);//rysowanie eq gracza
        npc->eq->draw(bufor, npcEQ.x, npcEQ.y);//rysowanie eq NPC
        blit(bufor, screen, 0, 0, 0, 0, bufor->w, bufor->h);//przyrysowuje bufor na ekran
    }
}

inline void cHero::checkItemInfo(cEQ * e)
{
    //sprawdzenie czy myszka jest nad jakims itemem
    if (mouse_x >= e->pos->x && mouse_x < e->pos->x + e->size->x * ITEM_SIZE && mouse_y >= e->pos->y && mouse_y < e->pos->y + e->size->y * ITEM_SIZE)
    {
        if (e->onItem == NULL)
            e->onItem = new sPoint((int)((mouse_x - e->pos->x - 1) / ITEM_SIZE), (int)((mouse_y - e->pos->y - 1) / ITEM_SIZE));
        else
        {
            e->onItem->x = (int)((mouse_x - e->pos->x - 1) / ITEM_SIZE);
            e->onItem->y = (int)((mouse_y - e->pos->y - 1) / ITEM_SIZE);
        }
    } else {delete e->onItem; e->onItem = NULL;}
}

inline void cHero::checkDragItem(cEQ * hEQ, cEQ * npcEQ, bool &MOUSE_FREE, sPoint &selItem, bool &fromHero)
{
    int x, y;

    if (mouse_b == 2)
    {
        if (MOUSE_FREE)
        {
            MOUSE_FREE = false;

            if (mouse_x >= hEQ->pos->x && mouse_x < hEQ->pos->x + hEQ->size->x * ITEM_SIZE)
            {
                if (mouse_y >= hEQ->pos->y && mouse_y < hEQ->pos->y + hEQ->size->y * ITEM_SIZE)
                {
                    x = mouse_x - hEQ->pos->x; x /= ITEM_SIZE;
                    y = mouse_y - hEQ->pos->y; y /= ITEM_SIZE;

                    if (hEQ->item[x][y]->id != EMPTY_ITEM)
                    {
                        sPoint p = npcEQ->getFreeSpace();
                        hEQ->itemSell(x, y, p.x, p.y, npcEQ);
                    }
                }
            }

            if (mouse_x >= npcEQ->pos->x && mouse_x < npcEQ->pos->x + npcEQ->size->x * ITEM_SIZE)
            {
                if (mouse_y >= npcEQ->pos->y && mouse_y < npcEQ->pos->y + npcEQ->size->y * ITEM_SIZE)
                {
                    x = mouse_x - npcEQ->pos->x; x /= ITEM_SIZE;
                    y = mouse_y - npcEQ->pos->y; y /= ITEM_SIZE;

                    if (npcEQ->item[x][y]->id != EMPTY_ITEM)
                    {
                        sPoint p = hEQ->getFreeSpace();
                        hEQ->itemBuy(x, y, p.x, p.y, npcEQ);
                    }
                }
            }
        }
    }
    else if (mouse_b == 1)
    {
        if (MOUSE_FREE)
        {
            MOUSE_FREE = false;

            if (mouse_x >= hEQ->pos->x && mouse_x < hEQ->pos->x + hEQ->size->x * ITEM_SIZE)
            {
                if (mouse_y >= hEQ->pos->y && mouse_y < hEQ->pos->y + hEQ->size->y * ITEM_SIZE)
                {
                    x = mouse_x - hEQ->pos->x; x /= ITEM_SIZE;
                    y = mouse_y - hEQ->pos->y; y /= ITEM_SIZE;

                    if (selItem.x == -1 && selItem.y == -1)
                    {
                        //jesli zaden item nie jest zaznaczony i obecnie zaznaczany jest itemem
                        if (hEQ->item[x][y]->id != EMPTY_ITEM)
                        {
                            selItem.x = x;
                            selItem.y = y;
                            fromHero = true;
                        }
                    }
                    else
                    {
                        //jesli hero przenosi w swoim EQ z miejsca na miejsce
                        if (fromHero)
                        {
                            int a = selItem.x,
                                b = selItem.y;

                            //jesli juz tam jest jakis item to je zamienia miejscami
                            if (hEQ->item[x][y]->id != EMPTY_ITEM)
                            {
                                cItem i(hEQ->item[a][b]->id, hEQ->item[a][b]->count);
                                hEQ->item[a][b]->id = hEQ->item[x][y]->id;
                                hEQ->item[a][b]->count = hEQ->item[x][y]->count;
                                hEQ->item[x][y]->id = i.id;
                                hEQ->item[x][y]->count = i.count;
                                selItem.x = selItem.y = -1;
                            } else
                            //jesli nie to stawia tam ten item
                            {
                                hEQ->item[x][y]->id = hEQ->item[a][b]->id;
                                hEQ->item[x][y]->count = hEQ->item[a][(int)selItem.y]->count;
                                hEQ->item[a][b]->id = EMPTY_ITEM;
                                hEQ->item[a][b]->count = 0;
                                selItem.x = selItem.y = -1;
                            }
                        }
                        else//jesli przenosi ze sklepu
                        {
                            if (hEQ->item[x][y]->id == EMPTY_ITEM)
                            {
                                hEQ->itemBuy(selItem.x, selItem.y, x, y, npcEQ);
                            }
                            selItem.x = selItem.y = -1;
                        }
                    }
                }
            }

            ///OBSLUGA EQ NPC
            if (mouse_x >= npcEQ->pos->x && mouse_x < npcEQ->pos->x + npcEQ->size->x * ITEM_SIZE)
            {
                if (mouse_y >= npcEQ->pos->y && mouse_y < npcEQ->pos->y + npcEQ->size->y * ITEM_SIZE)
                {
                    x = mouse_x - npcEQ->pos->x; x /= ITEM_SIZE;
                    y = mouse_y - npcEQ->pos->y; y /= ITEM_SIZE;

                    if (selItem.x == -1 && selItem.y == -1)
                    {
                        //jesli zaden item nie jest zaznaczony i obecnie zaznaczany jest itemem
                        if (npcEQ->item[x][y]->id != EMPTY_ITEM)
                        {
                            selItem.x = x;
                            selItem.y = y;
                            fromHero = false;
                        }
                    }
                    else
                    {
                        //jesli przenosi od hero (czyli sprzedaje)
                        if (fromHero)
                        {
                            if (npcEQ->item[x][y]->id == EMPTY_ITEM)
                            {
                                hEQ->itemSell(selItem.x, selItem.y, x, y, npcEQ);
                            }
                            selItem.x = selItem.y = -1;
                        }
                        else
                        {
                            int a = selItem.x,
                                b = selItem.y;

                            //jesli juz tam jest jakis item to je zamienia miejscami
                            if (npcEQ->item[x][y]->id != EMPTY_ITEM)
                            {
                                cItem i(npcEQ->item[a][b]->id, npcEQ->item[a][b]->count);
                                npcEQ->item[a][b]->id = npcEQ->item[x][y]->id;
                                npcEQ->item[a][b]->count = npcEQ->item[x][y]->count;
                                npcEQ->item[x][y]->id = i.id;
                                npcEQ->item[x][y]->count = i.count;
                                selItem.x = selItem.y = -1;
                            } else
                            //jesli nie to stawia tam ten item
                            {
                                npcEQ->item[x][y]->id = npcEQ->item[a][b]->id;
                                npcEQ->item[x][y]->count = npcEQ->item[a][b]->count;
                                npcEQ->item[a][b]->id = EMPTY_ITEM;
                                npcEQ->item[a][b]->count = 0;
                                selItem.x = selItem.y = -1;
                            }
                        }
                    }
                }
            }
        }
    } else MOUSE_FREE = true;
}
